package com.vogue.supplymall.common.config;

import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import com.vogue.sprakka.ActorStarter;
import com.vogue.sprakka.BeanFieldProcessor;
import com.vogue.sprakka.remote.ExportServerInfo;
import com.vogue.sprakka.remote.ServerInfo;
import com.vogue.supplymall.common.domain.tool.GsonUtils;
import com.vogue.supplymall.common.domain.tool.PackageUtil;

/**
 * akka服务发布基类
 * @author cy
 *
 */
public abstract class AkkaBaseConfig extends WebMvcConfigurerAdapter {
    
    public static final Logger LOGGER_AKKABASECONFIG = LoggerFactory.getLogger(AkkaBaseConfig.class);

    @Autowired
    private ApplicationContext applicationContext;
    /**
     * 最小服务数
     */
    private final static Integer EXPORTSERVERINFOMIN = 2;
    /**
     * 最大服务数
     */
    private final static Integer EXPORTSERVERINFOMAX = 10;
    /**
     * 框架最大容量
     */
    private final static String MAXIMUMFRAMESIZE = "12800000b";
    /**
     * akka服务配置参数
     */
    private static String configStr = "akka.loglevel = %s\n"
        + "akka.actor.provider=akka.remote.RemoteActorRefProvider \n"
        + "akka.remote.netty.tcp.port= %s \n"
        + "akka.remote.netty.tcp.hostname= %s \n"
        + "akka.remote.netty.tcp.maximum-frame-size=%s \n";

    /**
     * @return 服务启动调试等级
     */
    public abstract String getAkkaLoglevel();
    /**
     * @return 服务启动端口
     */
    public abstract String getAkkaRemoteNettyTcpPort();
    /**
     * @return 服务启动ip
     */
    public abstract String getAkkaRemoteNettyTcpHostname();
    /**
     * @return 服务启动名称
     */
    public abstract String getAkkaRemoteNettyTcpServername();
    /**
     * @return 服务启动检测的包
     */
    public abstract String getAkkaScanPackage();
    /**
     * @return 作为客户端启动配置的信息JSON串
     */
    public abstract String getAkkaClientInfo();
    /**
     * @return 最小服务数
     */
    public abstract Integer getAkkaRemoteMin();
    /**
     * @return 最大服务数
     */
    public abstract Integer getAkkaRemoteMax();
    /**
     * @return 框架最大容量
     */
    public abstract String getAkkaRemoteMaxSize();
    
    /**
     * 初始化客户端
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public void initAkkaClient(ActorStarter actorStarter) {
        // client
        if (StringUtils.isNotEmpty(getAkkaClientInfo())) {
            List<Map> clientList = GsonUtils.GSON.fromJson(getAkkaClientInfo(), List.class);
            for (Map clientMap : clientList) {
                List<Map> serverClassList = (List<Map>) clientMap.get("classlist");
                for (Map<String, String> classMap : serverClassList) {
                    ServerInfo sf = new ServerInfo();
                    sf.setSystemName((String) clientMap.get("servername"));
//                    sf.setServerName("/user/wuXiangPayment");
                    sf.setAddress((String) clientMap.get("serverhostname") + ":" + (String) clientMap.get("serverport"));
                    try {
                        sf.setInterfaceName(Class.forName((String) classMap.get("name")));
                    } catch (ClassNotFoundException e) {
                        LOGGER_AKKABASECONFIG.error(">>>无法找到类:{}", (String) classMap.get("name"));
                    }
//                    sf.setServerId("gtt");
                 // actorStarter.putRemoteService("remotesample.server.IGreeter",sf);
                    actorStarter.putRemoteService(sf);
                }
            }
        } else {
            if (actorStarter == null) {
                LOGGER_AKKABASECONFIG.error("无法初始化akka客户端ActorStarter为空");
            } 
            if (getAkkaScanPackage() != null) {
                LOGGER_AKKABASECONFIG.error("无法初始化akka客户端缺少必要的配置信息");
            }
        }
    }
    
    /**
     * 初始化服务端
     */
    public void initAkkaServer() {
        ActorStarter actorStarter = applicationContext.getBean(ActorStarter.class);
        // server
        if (actorStarter != null && getAkkaScanPackage() != null) {
            String [] packageArr = getAkkaScanPackage().split(",");
            for (String packageStr : packageArr) {
                List<String> serverClassList = PackageUtil.getClassName(packageStr);
                LOGGER_AKKABASECONFIG.info("akka初始化对外接口的个数为：{}", serverClassList.size());
                for (String className : serverClassList) {
                    try {
                        ExportServerInfo esf = new ExportServerInfo();
                        esf.setService(applicationContext.getBean(Class.forName(className)));
                        esf.setMin(getAkkaRemoteMin() == null ? EXPORTSERVERINFOMIN : getAkkaRemoteMin());
                        esf.setMax(getAkkaRemoteMax() == null ? EXPORTSERVERINFOMAX : getAkkaRemoteMax());
                        actorStarter.addExportService(esf);
                    } catch (BeansException e) {
                        LOGGER_AKKABASECONFIG.error("akka启动服务错误：{}", e.getMessage());
                    } catch (ClassNotFoundException e) {
                        LOGGER_AKKABASECONFIG.error("akka启动服务找不到类：{}", className);
                    }
                } 
            }     
            actorStarter.publishServices();
        } else {
            if (actorStarter == null) {
                LOGGER_AKKABASECONFIG.error("无法初始化akka服务端ActorStarter为空");
            } 
            if (getAkkaScanPackage() != null) {
                LOGGER_AKKABASECONFIG.error("无法初始化akka服务端缺少必要的配置信息");
            }
        }        
    }
    
    /**
     * Actor system singleton for this application.
     * @return ActorStarter
     */
    @Bean
    public ActorStarter actorStarter() {
        if (StringUtils.isNotEmpty(getAkkaLoglevel())) {
         // Config config = ConfigFactory.load("test/reference.conf");
            Config config = ConfigFactory
                    .parseString(
//                            "akka.loglevel = debug\n" +
//                    "akka.actor.provider=akka.remote.RemoteActorRefProvider \n" +
//                    "akka.remote.netty.tcp.port= 2554 \n" +
//                    "akka.remote.netty.tcp.hostname= 127.0.0.1 \n" +
//                    "akka.remote.netty.tcp.maximum-frame-size=12800000b \n"
                    String.format(configStr,
                            getAkkaLoglevel(),
                            getAkkaRemoteNettyTcpPort(),
                            getAkkaRemoteNettyTcpHostname(),
                            getAkkaRemoteMaxSize() != null ? getAkkaRemoteMaxSize() : MAXIMUMFRAMESIZE) + config())
                    .withFallback(ConfigFactory.load());
            // Create an Akka system
            ActorStarter actorStarter = new ActorStarter(getAkkaRemoteNettyTcpServername(), config);
//            SpringExtProvider.get(actorStarter.getActorSystem()).initialize(applicationContext);
            initAkkaClient(actorStarter);
            return actorStarter;
        } else {
            return null;
        }
    }

    // required
    /**
     * @return BeanFieldProcessor
     */
    @Bean
    public BeanFieldProcessor fieldProcessor() {
//        return new BeanFieldProcessor(actorStarter());
        return new BeanFieldProcessor(applicationContext.getBean(ActorStarter.class));
    }

    /**
     * @return akka服务启动配置字符串
     */
    private static String config() {
        return "\"akka\": { " + "\"actor\" : {" + "\"allow-java-serialization\":\"off\"," + "\"serializers\": {"
                + "\"myproto\" : \"com.vogue.sprakka.utils.ProtoBufSerializer\"" + "},"
                + "\"serialization-bindings\" : {" + "\"com.vogue.sprakka.post.Request\" : \"myproto\", "
                + "\"com.vogue.sprakka.post.Response\" : \"myproto\"" + "}" + " }" + "}";

        // return
        // "akka.actor.allow-java-serialization = off\n" +
        // "akka.actor.serializers.myproto = com.vogue.sprakka.utils.ProtoBufSerializer
        // \n" +
        // "akka.actor.serialization-bindings.\"com.vogue.sprakka.post.Request\" =
        // myproto\n" +
        // "akka.actor.serialization-bindings.\"com.vogue.sprakka.post.Response\" =
        // myproto\n" ;

        /*
         * return
         * "akka.actor.serializers.proto = akka.remote.serialization.ProtobufSerializer \n "
         * + "akka.actor.serializers.java = akka.serialization.JavaSerializer \n " +
         * "akka.actor.serializers.kryo = com.romix.akka.serialization.kryo.KryoSerializer \n "
         * + "akka.actor.serialization-bindings.\"java.io.Serializable\" = none \n" +
         * "akka.actor.serialization-bindings.\"com.vogue.sprakka.post.Request\" = kryo \n"
         * +
         * "akka.actor.serialization-bindings.\"com.vogue.sprakka.post.Response\" = kryo \n"
         * + "akka.actor.kryo.type = graph \n" +
         * "akka.actor.kryo.idstrategy = incremental \n" +
         * "akka.actor.kryo.buffer-size = 4096\n" +
         * "akka.actor.kryo.max-buffer-size = -1\n" +
         * "akka.actor.kryo.use-manifests = false\n" +
         * "akka.actor.kryo.use-unsafe = false\n" +
         * "akka.actor.kryo.post-serialization-transformations = \"lz4,aes\" \n" +
         * "akka.actor.kryo.encryption.aes.mode = \"AES/CBC/PKCS5Padding\" \n" +
         * "akka.actor.kryo.encryption.aes.key = j68KkRjq21ykRGAQ \n" +
         * "akka.actor.kryo.encryption.aes.IV-length = 16 \n" +
         * "akka.actor.kryo.implicit-registration-logging = false\n" +
         * "akka.actor.kryo.kryo-trace = true\n" +
         * "akka.actor.kryo.resolve-subclasses = false\n" +
         * "akka.actor.kryo.classes = [com.vogue.sprakka.post.Request,com.vogue.sprakka.post.Response]\n"
         * + "akka.actor.kryo.mappings.\"com.vogue.sprakka.post.Request\"=20 \n" +
         * "akka.actor.kryo.mappings.\"com.vogue.sprakka.post.Response\"=21" ;
         */
    }
}
